package com.momo.demo10_txt;

import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.Date;

import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.queryParser.QueryParser;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.TopScoreDocCollector;
import org.apache.lucene.store.FSDirectory;
import org.apache.lucene.util.Version;
import org.junit.Test;

import com.momo.utils.LuceneUtils;

/**
 * Txt操作
 */
public class Indexer {
    private static String INDEX_DIR = "D:\\lucene\\index";// 保存索引文件的地方

    private static String DATA_DIR = "D:\\lucene\\data";// 将要搜索TXT文件的地方

    private static String KEYWORD = "机器人";

    private static int TOP_NUM = 100;

    /**
     * 创建Index
     */
    @Test
    public void createIndex() throws Exception {
        long start = new Date().getTime();
        int numIndexed = index(new File(INDEX_DIR), new File(DATA_DIR));
        long end = new Date().getTime();
        System.out.println("Indexing " + numIndexed + " files took " + (end - start) + " milliseconds");
    }

    /**
     * 搜索Index
     */
    @Test
    public void searchIndex() throws Exception {
        File indexDir = new File(INDEX_DIR);
        if (!indexDir.exists() || !indexDir.isDirectory()) {
            throw new Exception(indexDir + " does not exist or is not a directory.");
        }
        search(indexDir, KEYWORD);
    }

    /**
     * 索引dataDir下.txt文件，并储存在indexDir下，返回索引的文件数量
     */
    public static int index(File indexDir, File dataDir) throws IOException {
        if (!dataDir.exists() || !dataDir.isDirectory()) {
            throw new IOException(dataDir + " does not exist or is not a directory");
        }
        /*
		 * 创建IndexWriter对象, 第一个参数是Directory,也可以为：Directory dir = new
		 * SimpleFSDirectory(new File(indexDir)); 第二个是分词器, 第三个表示是否是创建,
		 * 如果为false为在此基础上面修改, 第四表示表示分词的最大值，比如说new MaxFieldLength(2)，
		 * 就表示两个字一分，一般用IndexWriter.MaxFieldLength.LIMITED
		 */
        IndexWriter indexWriter = new IndexWriter(FSDirectory.open(indexDir), LuceneUtils.analyzer, true, IndexWriter.MaxFieldLength.LIMITED);
        indexDirectory(indexWriter, dataDir);

        // 查看IndexWriter里面有多少个索引
        int numIndexed = indexWriter.numDocs();
        indexWriter.optimize();
        indexWriter.close();
        return numIndexed;
    }

    /**
     * 循环遍历dir下的所有.txt文件并进行索引
     */
    private static void indexDirectory(IndexWriter writer, File dir) throws IOException {
        File[] files = dir.listFiles();
        for (int i = 0; i < files.length; i++) {
            if (files[i].isDirectory()) {
                // 递归
                indexDirectory(writer, files[i]);
            } else if (files[i].getName().endsWith(".txt")) {
                indexFile(writer, files[i]);
            }
        }
    }

    /**
     * 对单个txt文件进行索引
     */
    private static void indexFile(IndexWriter writer, File f) throws IOException {
        if (f.isHidden() || !f.exists() || !f.canRead()) {
            return;
        }
        System.out.println("Indexing " + f.getCanonicalPath());
        Document doc = new Document();
        doc.add(new Field("contents", new FileReader(f)));
        doc.add(new Field("filename", f.getCanonicalPath(), Field.Store.YES, Field.Index.ANALYZED));
		/*
		 * Field.Index有五个属性，分别是： Field.Index.ANALYZED：分词索引
		 * Field.Index.NOT_ANALYZED：分词进行索引，如作者名，日期等，Rod Johnson本身为一单词，不再需要分词。
		 * Field.Index.NO：不进行索引，存放不能被搜索的内容如文档的一些附加属性如文档类型, URL等。
		 * Field.Index.NOT_ANALYZED_NO_NORMS：不使用分词索引，不使用存储规则。
		 * Field.Index.ANALYZED_NO_NORMS：使用分词索引，不使用存储规则。
		 */
        writer.addDocument(doc);
    }

    /**
     * 查詢
     *
     * @param indexDir索引目录地址
     * @param q              要查询的字符串
     */
    public static void search(File indexDir, String q) throws Exception {
        // 创建 IndexSearcher对象，相比IndexWriter对象，这个参数就要提供一个索引的目录就行了
        IndexSearcher indexSearch = new IndexSearcher(FSDirectory.open(indexDir), true);// read-only
        // 在建立索引时,存在IndexWriter对象中的
        String field = "contents";
        // 创建QueryParser对象, 第一个参数表示Lucene的版本, 第二个表示搜索Field的字段, 第三个表示搜索使用分词器
        QueryParser parser = new QueryParser(Version.LUCENE_30, field, LuceneUtils.analyzer);
        Query query = parser.parse(q);// 生成Query对象
        TopScoreDocCollector collector = TopScoreDocCollector.create(TOP_NUM, false);
        long start = new Date().getTime();
        indexSearch.search(query, collector);
        // 搜索结果TopScoreDocCollector里面有 TopDocs,TopDocs里面有scoreDocs[]数组，里面保存着索引值.
        ScoreDoc[] hits = collector.topDocs().scoreDocs;
        System.out.println("找到了" + hits.length + "个");

        // 循环ScoreDoc数据，并使用indexSearch.doc方法把Document还原，再拿出对应的字段的值
        for (int i = 0; i < hits.length; i++) {
            Document doc = indexSearch.doc(hits[i].doc);
            System.out.println("doc中的文件名：" + doc.getField("filename"));
            System.out.println("hits中的信息：" + hits[i].toString());
        }
        indexSearch.close();
        long end = new Date().getTime();
        System.out.println("Found " + collector.getTotalHits() + " document(s) (in " + (end - start) + " milliseconds) that matched query '" + q + "':");
    }
}